//-----------------------------------------------------------------------
// <copyright file="ConnectionManager.cs" company="n.a.">
//     Copyright Dave999. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
/*
    New for 2012: Module change log
    Moving this class to a "resource" pattern 
    ie. private constructor and a single instance, Special resources Namespace

    2013: adding a property to connection encrpyted T/F
*/
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "To be returned to .")]

namespace DbView.Resources
{
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Xml;

	/// <summary>
	/// Manages the list of connections
	/// </summary>
	public class ConnectionListMgr : IEnumerable
	{
		// the singleton object
		private static ConnectionListMgr implementation = null;

		private string currentDriver = string.Empty;
		private List<ConnectionInfo> connections = new List<ConnectionInfo>();

		public ConnectionListMgr()
		{
		}

		// access to the singleton
		public static ConnectionListMgr Instance
		{
			get
			{
				if (implementation == null)
				{
					implementation = new ConnectionListMgr();
				}

				return implementation;
			}
		}

		public int Count
		{
			get { return this.connections.Count; }
		}

		public string CurrentDriverName
		{
			get { return this.currentDriver; }
			set { this.currentDriver = value; }
		}

		public ConnectionInfo this[string name]
		{
			get
			{
				foreach (ConnectionInfo ci in this)
				{
					if (ci.Driver == this.CurrentDriverName)
					{
						if (ci.Name == name)
						{
							return ci;
						}
					}
				}

				string errMessage = string.Format("Unknown Connection name: {0}", name);
				throw new DbViewApplicationException(errMessage);
			}
		}

		// In case modifying the above to search all drivers causes a problem with old code
		// here is one to use when querying history. Up to the client to test the driver
		public ConnectionInfo GetConnectionInfo(string name)
		{
			// prefer the current driver
			foreach (ConnectionInfo ci in this)
			{
				if (ci.Name == name)
				{
					return ci;
				}
			}

			// OK now try the others
			foreach (ConnectionInfo ci in this.connections)
			{
				if (ci.Name == name)
				{
					return ci;
				}
			}

			// Nope: return null hope the client can cope
			return null;
		}

		public bool Load()
		{
			this.connections.Clear();

			// new stuff
			string xpath = string.Format("//{0}/{1}", ConfigDoc.CFGELM_CONNECTIONS, ConfigDoc.CFGELM_CONNECTION);
			XmlNodeList connNodes = ConfigDoc.Instance.SelectNodes(xpath);
			foreach (XmlNode connNode in connNodes)
			{
				ConnectionInfo ci = new ConnectionInfo(connNode);
				this.connections.Add(ci);
			}

			return true;
		}

		public void UpdateConnectionList(ConnectionInfo[] aci)
		{
			// end remove nodes

			// remove all for the current driver
			List<ConnectionInfo> deleteList = new List<ConnectionInfo>();
			foreach (ConnectionInfo ci in this.connections)
			{
				if (ci.Driver == this.CurrentDriverName)
				{
					deleteList.Add(ci);
				}
			}

			foreach (ConnectionInfo ci in deleteList)
			{
				this.connections.Remove(ci);
			}

			foreach (ConnectionInfo ci in aci)
			{
				this.connections.Add(ci);
				ci.RefreshNode(); // adds any new nodes to the doc.
				ci.MoveToEnd();   // preserves order 
			}
		}

		// Set default encryption option for entered passwords
		public void SetEncryption(bool encryption)
		{
			ConfigDoc.Instance.AddAttribute(
											ConfigDoc.Instance.Connections,
											ConfigDoc.CFGATT_ENCRYPTION,
											encryption ? "Y" : "N");
		}

		public bool GetEncryptionSetting()
		{
			return DbView.Resources.ConfigDoc.Instance.AttributeValue(ConfigDoc.Instance.Connections, ConfigDoc.CFGATT_ENCRYPTION, false);
		}

		// here we need get and set current connector
		// and get driver. This is used for restarting the app to bootstrap the 
		// choice of driver.
		public void SetLastUsedConnection(ConnectionInfo ci)
		{
			// store as last used
			ConfigDoc.Instance.AddAttribute(
											ConfigDoc.Instance.Connections,
											ConfigDoc.CFGATT_LASTUSED,
											ci.Name);
			ConfigDoc.Instance.AddAttribute(
											ConfigDoc.Instance.Connections,
											ConfigDoc.CFGATT_LASTUSEDDRIVER,
											ci.Driver);
		}

		public ConnectionInfo GetLastUsedConnection()
		{
			// store as last used
			string connName = ConfigDoc.Instance.AttributeValue(
											ConfigDoc.Instance.Connections,
											ConfigDoc.CFGATT_LASTUSED,
											string.Empty);
			string connDriver = ConfigDoc.Instance.AttributeValue(
											ConfigDoc.Instance.Connections,
											ConfigDoc.CFGATT_LASTUSEDDRIVER,
											string.Empty);

			// null means not set or something wrong
			if (connName.Length == 0)
			{
				return null;
			}

			// search for name. client should figure out what to do about driver.
			ConnectionInfo fallBack = null;
			foreach (ConnectionInfo ci in this.connections)
			{
				if (ci.Name == connName)
				{
					if (ci.Driver == connDriver)
					{
						return ci; // match on name and driver (normal)
					}

					// keep the last connection that matched the name.
					// the name will normally be unique anyway.
					// here during transition phase.
					fallBack = ci;
				}
			}

			return fallBack;
		}

		// enumeration interface Troelsen 219 on
		public IEnumerator GetEnumerator()
		{
			return new Iterator(this.connections, this.CurrentDriverName);
		}

		public class Iterator : IEnumerator
		{
			private int position = 0;
			private List<ConnectionInfo> connections = new List<ConnectionInfo>();

			public Iterator(List<ConnectionInfo> theConnections, string driverName)
			{
				foreach (ConnectionInfo ci in theConnections)
				{
					if (ci.Driver == driverName)
					{
						this.connections.Add(ci);
					}
				}
			}

			public object Current
			{
				get
				{
					if (this.position > this.connections.Count)
					{
						throw new System.ArgumentOutOfRangeException();
					}

					int idx = this.position - 1;
					ConnectionInfo ci = (ConnectionInfo)this.connections[idx];
					return ci;
				}
			}

			// implement MoveNext(), Reset() and Current()
			public bool MoveNext()
			{
				if (this.position >= this.connections.Count)
				{
					this.Reset();
					return false;
				}
				++this.position;
				return true;
			}

			public void Reset()
			{
				this.position = 0;
			}
		}
	}
}
